//  Copyright Druid Mechanics


#include "AbilitySystem/AuraAttributeSet.h"
#include "GameplayEffectExtension.h"
#include "GameFramework/Character.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "AuraGameplayTags.h"
#include "AuraAbilityTypes.h"
/**
 * UAbilitySystemComponent: 主要组件，由C++编写，代码里有很多方法是蓝图未实现的，用于处理和Ability、Effects、Attributes交互逻辑的组件。
 * 一个易于与AbilitySystem的3个方面进行交互的组件:
 *
 * GameplayAbilities: 角色的技能，包括攻击、疾跑、施法、翻滚、使用道具等，但不包括基础移动和UI
 * - 提供一个给予或分配能被用于(玩家或AI)的能力的方法。
 * - 提供实例化的能力的管理(一些事情必须抓住它们)。
 * - 提供复制功能。
 * - 能力状态必须经常在UGameplayAbility复制它自己，但是UAbilitySystemComponent为实际活跃的能力提供了RPC复制。
 *
 * GameplayEffects: 用于修改属性，如增加50移动速度10s；还能配合GA实现更多玩法。
 * - 提供一个FActiveGameplayEffectsContainer用于保存活跃的GameplayEffects。
 * - 提供请求GameplayEffects到节点或自身的方法。
 * - 提供FActiveGameplayEffectsContainers参数信息的封装。
 * - 提供清空或移除GameplayEffects的方法。
 *
 * GameplayAttributes: 角色身上可以用float表示的属性，如生命值、体力值、魔力值等
 * - 提供分配和初始化属性的集合。
 * - 提供获取属性的方法。
 *
 * ASC(Ability System Component) 主要组件，由C++编写，代码里有很多方法是蓝图未实现的。
 * GA(Gameplay Abilities) 角色的技能，包括攻击、疾跑、施法、翻滚、使用道具等，但不包括基础移动和UI。
 * AS(Attribute Set) 角色身上可以用float表示的属性，如生命值、体力值、魔力值等。
 * GE(Gameplay Effects) 用于修改属性，如增加50移动速度10s；还能配合GA实现更多玩法。GE是修改Attribute的唯一渠道！GE就是一张数据表，不负责逻辑处理，定义Attribute修改的值。
 * GC(Gameplay Cues) 播放特效、音效等。
 * 
 * LINK: https://zhuanlan.zhihu.com/p/486808688
 */
#include "AbilitySystemComponent.h"
#include "AbilitySystem/AuraAbilitySystemLibrary.h"
#include "Interaction/CombatInterface.h"
#include "Interaction/PlayerInterface.h"
#include "Net/UnrealNetwork.h"
#include "Player/AuraPlayerController.h"
// #include "Interaction/PlayerInterface.h"

UAuraAttributeSet::UAuraAttributeSet()
{
	const FAuraGameplayTags& GameplayTags = FAuraGameplayTags::Get();
	/**
	 * BindStatic: 事件代理绑定函数，适用于静态函数。
	 * FAttributeSignature IntelligenceDelegate;
	 * IntelligenceDelegate.BindStatic(UAuraAttributeSet::GetIntelligenceAttribute);
	 * 这里本质上是将代理替换成了函数指针。
	 * LINK: https://www.runoob.com/w3cnote/cpp-func-pointer.html
	 */
	/**
	 * Primary Attributes
	 */
	TagsToAttributes.Add(GameplayTags.Attributes_Primary_Strength, GetStrengthAttribute);
	TagsToAttributes.Add(GameplayTags.Attributes_Primary_Intelligence, GetIntelligenceAttribute);
	TagsToAttributes.Add(GameplayTags.Attributes_Primary_Resilience, GetResilienceAttribute);
	TagsToAttributes.Add(GameplayTags.Attributes_Primary_Vigor, GetVigorAttribute);
	/**
	 * Secondary Attributes
	 */
	TagsToAttributes.Add(GameplayTags.Attributes_Secondary_Armor, GetArmorAttribute);
	TagsToAttributes.Add(GameplayTags.Attributes_Secondary_ArmorPenetration, GetArmorPenetrationAttribute);
	TagsToAttributes.Add(GameplayTags.Attributes_Secondary_BlockChance, GetBlockChanceAttribute);
	TagsToAttributes.Add(GameplayTags.Attributes_Secondary_HealthRegeneration, GetHealthRegenerationAttribute);
	TagsToAttributes.Add(GameplayTags.Attributes_Secondary_ManaRegeneration, GetManaRegenerationAttribute);
	TagsToAttributes.Add(GameplayTags.Attributes_Secondary_CriticalHitChance, GetCriticalHitChanceAttribute);
	TagsToAttributes.Add(GameplayTags.Attributes_Secondary_CriticalHitDamage, GetCriticalHitDamageAttribute);
	TagsToAttributes.Add(GameplayTags.Attributes_Secondary_CriticalHitResistance, GetCriticalHitResistanceAttribute);
	TagsToAttributes.Add(GameplayTags.Attributes_Secondary_MaxHealth, GetMaxHealthAttribute);
	TagsToAttributes.Add(GameplayTags.Attributes_Secondary_MaxMana, GetMaxManaAttribute);

	/**
	 * Resistance
	 */
	TagsToAttributes.Add(GameplayTags.Attributes_Resistance_Fire, GetFireResistanceAttribute);
	TagsToAttributes.Add(GameplayTags.Attributes_Resistance_Arcane, GetArcaneResistanceAttribute);
	TagsToAttributes.Add(GameplayTags.Attributes_Resistance_Lightning, GetLightningResistanceAttribute);
	TagsToAttributes.Add(GameplayTags.Attributes_Resistance_Physical, GetPhysicalResistanceAttribute);
}

void UAuraAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	/**
	 * DOREPLIFETIME_CONDITION的条件: 
	 * COND_InitialOnly: 属性只会在第一次连接时候同步，之后不再进行同步。
	 * COND_OwnerOnly: 属性只会同步给Actor的拥有者(Owner)。
	 * COND_SkipOwner: 属性会同步给 除了Actor拥有者之外其他的所有连接。         
	 * COND_SimulatedOnly: Actors属性只同步给simulated的Actor。
	 * COND_AutonomousOnly: Actors属性只同步给Autonomous的Actor。
	 * COND_SimulatedOrPhysics: 属性会同步给模拟(Simulated)类型和物理类型(设置了bRepPhysics标记的)的Actor。
	 * COND_InitialOrOwner: 属性会在第一次连接时候同步或者是同步给Actor的拥有者。
	 * COND_Custom: 自定义同步条件，使用SetCustomIsActiveOverride来开关同步。
	 */
	/**
	 * DOREPLIFETIME_CONDITION_NOTIFY: 按照具体的条件来处理复制变量的RepNotify函数。
	 * REPNOTIFY_Always: 在每次复制属性时在客户端上调用关联的OnRep() (只要给变量赋值，就会调用OnRep)。
	 * REPNOTIFY_OnChanged: 仅在属性值更改时在客户端上调用关联的OnRep() (若两次赋值都是一样的值，就不会调用OnRep)。
	 * LINK: https://dev.epicgames.com/documentation/zh-cn/unreal-engine/replicate-actor-properties-in-unreal-engine
	 */
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, Health, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, MaxHealth, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, Mana, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, MaxMana, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, Strength, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, Intelligence, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, Resilience, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, Vigor, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, Armor, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, ArmorPenetration, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, BlockChance, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, CriticalHitChance, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, CriticalHitDamage, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, CriticalHitResistance, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, HealthRegeneration, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, ManaRegeneration, COND_None, REPNOTIFY_Always);

	/**
	 * Resistance
	 */
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, FireResistance, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, LightningResistance, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, ArcaneResistance, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAuraAttributeSet, PhysicalResistance, COND_None, REPNOTIFY_Always);
}

void UAuraAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
	Super::PreAttributeChange(Attribute, NewValue);
	if (Attribute == GetHealthAttribute())
	{
		NewValue = FMath::Clamp(NewValue, 0.f, GetMaxHealth());
	}
	if (Attribute == GetManaAttribute())
	{
		NewValue = FMath::Clamp(NewValue, 0.f, GetMaxMana());
	}
}

void UAuraAttributeSet::SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props) const
{
	/**
 * GameplayEffectExtension.h文件仍为旧版本。FGameplayEffectModcallback数据在整个系统中使用，但原始的GameplayEffectExtension类已被删除。
 * 保留此头文件是为了避免破坏必须为FGameplayEffectModcallbackData包含它的项目。
 */
	/**
	 * FGameplayModifierEvaluatedData: 描述属性修改中发生的情况的数据。这将传递给能力集回调。
	 * struct FGameplayModifierEvaluatedData&	EvaluatedData;
	 * Data.EvaluatedData: 返回应用节点的flag或计算数据。
	 * Data.EvaluatedData.Attribute: 修改的哪个属性。
	 * Data.EvaluatedData.Magnitude: 应用属性的原生magnitude，这是在限制之前生成。
   */
	/**
	 * EffectSpec: GE的Source(源)的Spec结构体对象。
	 */
	/**
	 * GetContext(): 返回Source(源)的GE上下文内容句柄。
	 */
	/**
   * Source = causer of the effect, Target = target of the effect (owner of this AS).
   */
	Props.EffectContextHandle = Data.EffectSpec.GetContext();
	/**
	 * GetOriginalInstigatorAbilitySystemComponent: 返回整个链的开始的instigator的ASC。
	 */
	Props.SourceASC = Props.EffectContextHandle.GetOriginalInstigatorAbilitySystemComponent();
	/**
	 * IsValid: 若果一个对象的共享指针是实际存在的，则返回true。
	 */
	/**
   * AbilityActorInfo: 缓存有关所属Actor的数据，这些Actor需要经常访问(移动组件、网格组件、动画实例等)。
   */
	if (IsValid(Props.SourceASC) && Props.SourceASC->AbilityActorInfo.IsValid() && Props.SourceASC->AbilityActorInfo->
		AvatarActor.IsValid())
	{
		/**
		 * AvatarActor: 所有者的物理代表，用于节点和动画。它经常为空。
		 */
		Props.SourceAvatarActor = Props.SourceASC->AbilityActorInfo->AvatarActor.Get();
		/**
		 * PlayerController: 关联到所有者Actor的玩家控制器，他经常返回空。
		 */
		Props.SourceController = Props.SourceASC->AbilityActorInfo->PlayerController.Get();
		/**
		 * 这里可能存在Source(源)的控制器不存在，但是Source(源)的AvatarActor存在的情况。
		 */
		if (Props.SourceController == nullptr && Props.SourceAvatarActor != nullptr)
		{
			if (const APawn* Pawn = Cast<APawn>(Props.SourceAvatarActor))
			{
				Props.SourceController = Pawn->GetController();
			}
		}
		if (Props.SourceController)
		{
			/**
			 * 源码: Character = (Pawn ? Cast<ACharacter>(Pawn) : NULL);
			 */
			Props.SourceCharacter = Props.SourceController->GetCharacter();
		}
	}
	if (IsValid(&Data.Target) && Data.Target.AbilityActorInfo.IsValid() && Data.Target.AbilityActorInfo->AvatarActor.
	                                                                            IsValid())
	{
		/**
     * Target: 返回要要应用GE的节点。
     */
		Props.TargetAvatarActor = Data.Target.AbilityActorInfo->AvatarActor.Get();
		Props.TargetController = Data.Target.AbilityActorInfo->PlayerController.Get();
		/**
		 * Cast<ACharacter>(TargetActor): 这里用这个方式是为了说明获取的几种方式。
		 * 注意: 如果能发生GE的Target，则玩家控制器和角色都是存在。
		 * 猜测的另一种可能，如果作用效果产生了，那Character是一定存在的，不然作用给谁。
		 */
		Props.TargetCharacter = Cast<ACharacter>(Props.TargetAvatarActor);
		Props.TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(Props.TargetAvatarActor);
	}
}

/**
 * 在修改BaseValue时进行一系列的操作。
 */
void UAuraAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
	Super::PostGameplayEffectExecute(Data);
	FEffectProperties Props;
	SetEffectProperties(Data, Props);
	if (Props.TargetCharacter->Implements<UCombatInterface>() && ICombatInterface::Execute_IsDead(Props.TargetCharacter))
	{
		return;
	}
	if (Data.EvaluatedData.Attribute == GetHealthAttribute())
	{
		SetHealth(FMath::Clamp(GetHealth(), 0.f, GetMaxHealth()));
	}
	if (Data.EvaluatedData.Attribute == GetManaAttribute())
	{
		SetMana(FMath::Clamp(GetMana(), 0.f, GetMaxMana()));
	}
	if (Data.EvaluatedData.Attribute == GetIncomingDamageAttribute())
	{
		HandleIncomingDamage(Props);
	}
	if (Data.EvaluatedData.Attribute == GetIncomingXPAttribute())
	{
		HandleIncomingXP(Props);
	}
}

void UAuraAttributeSet::HandleIncomingDamage(const FEffectProperties& Props)
{
	const float LocalInComingDamage = GetIncomingDamage();
	SetIncomingDamage(0.f);
	if (LocalInComingDamage > 0.f)
	{
		const float NewHealth = GetHealth() - LocalInComingDamage;
		SetHealth(FMath::Clamp(NewHealth, 0.f, GetMaxHealth()));
		const bool bFatal = NewHealth <= 0.f;
		if (bFatal)
		{
			
			ICombatInterface* CombatInterface = Cast<ICombatInterface>(Props.TargetAvatarActor);
			if (CombatInterface)
			{
				CombatInterface->Die(UAuraAbilitySystemLibrary::GetDeathImpulse(Props.EffectContextHandle));
			}
			SendXPEvent(Props);
		}
		else
		{
			if(Props.TargetCharacter->Implements<UCombatInterface>() && !ICombatInterface::Execute_IsBeingShocked(Props.TargetCharacter))
			{
				FGameplayTagContainer TagContainer;
				TagContainer.AddTag(FAuraGameplayTags::Get().Effects_HitReact);
				/**
				 * TryActivateAbilitiesByTag:
				 * 尝试激活每一个与给定标签匹配且满足DoesAbilitySatisfyTagRequirements()条件的GA。
				 * 如果有任何尝试激活的操作，则返回true。可以激活多个GA，并且这些GA后续可能会激活失败。
				 * 如果bAllowRemoteActivation为true，将会远程激活本地/服务能力，如果为false则会尝试去本地激活能力。
				 */
				/**
				 * 未来的我如果看到句代码可能会产生疑惑，这里解答一下:
				 * 举个例子: 当AuraCharacter攻击AuraEnemy并产生效果。
				 * 在这个例子中，AuraCharacter作为Source，AuraEnemy作为Target。
				 * 换句话说:
				 * Source是GE的来源，产生者。
				 * Target是GE的目的，承受者。
				 */
				Props.TargetASC->TryActivateAbilitiesByTag(TagContainer);
			}
			const FVector& KnockbackForce = UAuraAbilitySystemLibrary::GetKnockbackForce(Props.EffectContextHandle);
			if (!KnockbackForce.IsNearlyZero(1.f))
			{
				/**
				 * LaunchCharacter: 设置一个进行中的启动速度在角色上。此速度将会在CharacterMovementComponent的Tick下次进行中并且设置failling状态。触发OnLaunched事件。
				 */
				Props.TargetCharacter->LaunchCharacter(KnockbackForce, true, true);
			}
		}
		const bool bBlock = UAuraAbilitySystemLibrary::IsBlockedHit(Props.EffectContextHandle);
		const bool bCritical = UAuraAbilitySystemLibrary::IsCriticalHit(Props.EffectContextHandle);
		ShowFloatingText(Props, LocalInComingDamage, bBlock, bCritical);
		if (UAuraAbilitySystemLibrary::IsSuccessfulDebuff(Props.EffectContextHandle))
		{
			Debuff(Props);
		}
	}
}

void UAuraAttributeSet::HandleIncomingXP(const FEffectProperties& Props)
{
	const float LocalIncomingXP = GetIncomingXP();
	SetIncomingXP(0.f);
	/**
	 * TODO: See if we should level up
	 */
	/**
	 * Source Character is the owner, since GA_ListenForEvents applies GE_EventBasedEffect, adding to IncomingXP.
	 */
	if (Props.SourceCharacter->Implements<UPlayerInterface>() && Props.SourceCharacter->Implements<UCombatInterface>())
	{
		const int32 CurrentLevel = ICombatInterface::Execute_GetPlayerLevel(Props.SourceCharacter);
		const int32 CurrentXP = IPlayerInterface::Execute_GetXP(Props.SourceCharacter);

		const int32 NewLevel = IPlayerInterface::Execute_FindLevelForXP(Props.SourceCharacter, CurrentXP + LocalIncomingXP);
		const int32 NumLevelUps = NewLevel - CurrentLevel;
		if (NumLevelUps > 0)
		{
			/**
			 * 这里传入CurrentLevel，是因为当角色升级之后，在新的等级时，是获取上一次等级针对他这次升到新的等级的奖励。
			 */
			int32 AttributePointsReward = 0;
			int32 SpellPointsReward = 0;

			for(int32 i = 0; i < NumLevelUps; i ++)
			{
				SpellPointsReward += IPlayerInterface::Execute_GetSpellPointsReward(Props.SourceCharacter, CurrentLevel + i);
				AttributePointsReward += IPlayerInterface::Execute_GetAttributePointsReward(Props.SourceCharacter, CurrentLevel + i);
			}

			IPlayerInterface::Execute_AddToPlayerLevel(Props.SourceCharacter, NumLevelUps);
			IPlayerInterface::Execute_AddToAttributePoints(Props.SourceCharacter, AttributePointsReward);
			IPlayerInterface::Execute_AddToSpellpoints(Props.SourceCharacter, SpellPointsReward);

			bTopoffHealth = true;
			bTopoffMana = true;

			IPlayerInterface::Execute_LevelUp(Props.SourceCharacter);
		}
		IPlayerInterface::Execute_AddToXP(Props.SourceCharacter, LocalIncomingXP);
	}
}

void UAuraAttributeSet::Debuff(const FEffectProperties& Props)
{
	const FAuraGameplayTags& GameplayTags = FAuraGameplayTags::Get();
	FGameplayEffectContextHandle EffectContext = Props.SourceASC->MakeEffectContext();
	EffectContext.AddSourceObject(Props.SourceAvatarActor);

	const FGameplayTag DamageType = UAuraAbilitySystemLibrary::GetDamageType(Props.EffectContextHandle);
	const float DebuffDamage = UAuraAbilitySystemLibrary::GetDebuffDamage(Props.EffectContextHandle);
	const float DebuffDuration = UAuraAbilitySystemLibrary::GetDebuffDuration(Props.EffectContextHandle);
	const float DebuffFrequency = UAuraAbilitySystemLibrary::GetDebuffFrequency(Props.EffectContextHandle);
	
	
	FString DebuffName = FString::Printf(TEXT("DynamicDebuff_%s"), *DamageType.ToString());
	/**
	 * GetTransientPackage: 返回transient顶级包，这对于临时存储不应保存的对象非常有用。
	 */
	/**
	 * NOTE: 猜测，在运行中新建的GE不会被加到GAS的复制的GE容器内，所以不会复制。
	 * NOTE: 我还是怀疑封装的GESpecHandle里封装了复制的东西。
	 */
	UGameplayEffect* Effect = NewObject<UGameplayEffect>(GetTransientPackage(), FName(DebuffName));
	Effect->DurationPolicy = EGameplayEffectDurationType::HasDuration;
	Effect->Period = DebuffFrequency;
	Effect->DurationMagnitude = FScalableFloat(DebuffDuration);
	const FGameplayTag DebuffTag = GameplayTags.DamageTypesToDebuffs[DamageType];
	/**
	 * InheritableOwnedTagsContainer: 这些标签将会Apply于GE所Apply的Actor。
	 * NOTE: UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = Tags, meta=(DisplayName="GrantedTags", Categories="OwnedTagsCategory"))，由此看出就是GrantedTags那个选项。
	 */
	Effect->InheritableOwnedTagsContainer.AddTag(DebuffTag);
	if (DebuffTag.MatchesTagExact(GameplayTags.Debuff_Stun))
	{
		Effect->InheritableOwnedTagsContainer.AddTag(GameplayTags.Player_Block_CursorTrace);
		Effect->InheritableOwnedTagsContainer.AddTag(GameplayTags.Player_Block_InputHeld);
		Effect->InheritableOwnedTagsContainer.AddTag(GameplayTags.Player_Block_InputPressed);
		Effect->InheritableOwnedTagsContainer.AddTag(GameplayTags.Player_Block_InputReleased);
	}
	Effect->StackingType = EGameplayEffectStackingType::AggregateBySource;
	Effect->StackLimitCount = 1;
	/**
	 * Modifiers: 将会影响这个效果目标的修改器数组。
	 */
	/**
	 * 这段代码值的学习一下，首先获取Modifiers的长度，然后吧，往数组里添加一个数据；
	 * 这就很神奇的是，这个数据点索引，就是Index，特别神奇。
	 */
	int32 Index = Effect->Modifiers.Num();
	Effect->Modifiers.Add(FGameplayModifierInfo());
	FGameplayModifierInfo& ModifierInfo =	Effect->Modifiers[Index];
	ModifierInfo.ModifierMagnitude = FScalableFloat(DebuffDamage);
	ModifierInfo.ModifierOp = EGameplayModOp::Additive;
	ModifierInfo.Attribute = UAuraAttributeSet::GetIncomingDamageAttribute();
	if (FGameplayEffectSpec* MutableSpec = new FGameplayEffectSpec(Effect, EffectContext, 1.f))
	{
		FAuraGameplayEffectContext* AuraContext = static_cast<FAuraGameplayEffectContext*>(MutableSpec->GetContext().Get());
		TSharedPtr<FGameplayTag> DebuffDamageType = MakeShareable<FGameplayTag>(new FGameplayTag(DamageType));
		AuraContext->SetDamageType(DebuffDamageType);
		Props.TargetASC->ApplyGameplayEffectSpecToSelf(*MutableSpec);
	}
}

void UAuraAttributeSet::PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue)
{
	Super::PostAttributeChange(Attribute, OldValue, NewValue);
	if (Attribute == GetMaxHealthAttribute() && bTopoffHealth)
	{
		SetHealth(GetMaxHealth());
		bTopoffHealth = false;
	}
	if (Attribute == GetMaxManaAttribute() && bTopoffMana)
	{
		SetMana(GetMaxMana());
		bTopoffMana = false;
	}
}

void UAuraAttributeSet::ShowFloatingText(const FEffectProperties& Props, float Damage, bool bBlockedHit,
                                         bool bCriticalHit) const
{
	if (Props.TargetCharacter != Props.SourceCharacter)
	{
		if (AAuraPlayerController* PC = Cast<AAuraPlayerController>(Props.SourceCharacter->Controller))
		{
			PC->ShowDamageNumber(Damage, Props.TargetCharacter, bBlockedHit, bCriticalHit);
			return;
		}
		if (AAuraPlayerController* PC = Cast<AAuraPlayerController>(Props.TargetCharacter->Controller))
		{
			PC->ShowDamageNumber(Damage, Props.TargetCharacter, bBlockedHit, bCriticalHit);
		}
	}
}

void UAuraAttributeSet::SendXPEvent(const FEffectProperties& Props)
{
	if (Props.TargetCharacter->Implements<UCombatInterface>())
	{
		const int32 TargetLevel = ICombatInterface::Execute_GetPlayerLevel(Props.TargetCharacter);
		const ECharacterClass TargetClass = ICombatInterface::Execute_GetCharacterClass(Props.TargetCharacter);
		const int32 XPReward = UAuraAbilitySystemLibrary::GetXPRewardForClassAndLevel(
			Props.TargetCharacter, TargetClass, TargetLevel);
		const FAuraGameplayTags& GameplayTags = FAuraGameplayTags::Get();
		FGameplayEventData Payload;
		Payload.EventTag = GameplayTags.Attributes_Meta_IncomingXP;
		Payload.EventMagnitude = XPReward;
		/**
		 * SendGameplayEventToActor: 此函数被用于触发在Actor上的GA携带着使用的payload数据。
		 * GetAbilitySystemComponent能获取此Actor的ASC组件，此函数就可以被执行，否则不会。
		 */
		UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(Props.SourceCharacter,
		                                                         GameplayTags.Attributes_Meta_IncomingXP, Payload);
	}
}

void UAuraAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth) const
{
	/**
	 * GAMEPLAYATTRIBUTE_REPNOTIFY: 这是一个帮助宏，被用于RepNotify函数去处理客户端预测修改属性。
	 */
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, Health, OldHealth);
}

void UAuraAttributeSet::OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, MaxHealth, OldMaxHealth);
}

void UAuraAttributeSet::OnRep_Mana(const FGameplayAttributeData& OldMana) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, Mana, OldMana);
}

void UAuraAttributeSet::OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, MaxHealth, OldMaxMana);
}

void UAuraAttributeSet::OnRep_Strength(const FGameplayAttributeData& OldStrength) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, Strength, OldStrength);
}

void UAuraAttributeSet::OnRep_Intelligence(const FGameplayAttributeData& OldIntelligence) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, Intelligence, OldIntelligence);
}

void UAuraAttributeSet::OnRep_Resilience(const FGameplayAttributeData& OldResilience) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, Resilience, OldResilience);
}

void UAuraAttributeSet::OnRep_Vigor(const FGameplayAttributeData& OldVigor) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, Vigor, OldVigor);
}

void UAuraAttributeSet::OnRep_Armor(const FGameplayAttributeData& OldArmor) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, Armor, OldArmor);
}

void UAuraAttributeSet::OnRep_ArmorPenetration(const FGameplayAttributeData& OldArmorPenetration) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, ArmorPenetration, OldArmorPenetration);
}

void UAuraAttributeSet::OnRep_BlockChance(const FGameplayAttributeData& OldBlockChance) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, BlockChance, OldBlockChance);
}

void UAuraAttributeSet::OnRep_CriticalHitChance(const FGameplayAttributeData& OldCriticalHitChance) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, CriticalHitChance, OldCriticalHitChance);
}

void UAuraAttributeSet::OnRep_CriticalHitDamage(const FGameplayAttributeData& OldCriticalHitDamage) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, CriticalHitDamage, OldCriticalHitDamage);
}

void UAuraAttributeSet::OnRep_CriticalHitResistance(const FGameplayAttributeData& OldCriticalHitResistance) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, CriticalHitResistance, OldCriticalHitResistance);
}

void UAuraAttributeSet::OnRep_HealthRegeneration(const FGameplayAttributeData& OldHealthRegeneration) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, HealthRegeneration, OldHealthRegeneration);
}

void UAuraAttributeSet::OnRep_ManaRegeneration(const FGameplayAttributeData& OldManaRegeneration) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, ManaRegeneration, OldManaRegeneration);
}

void UAuraAttributeSet::OnRep_FireResistance(const FGameplayAttributeData& OldFireResistance) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, FireResistance, OldFireResistance);
}

void UAuraAttributeSet::OnRep_ArcaneResistance(const FGameplayAttributeData& OldArcaneResistance) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, ArcaneResistance, OldArcaneResistance);
}

void UAuraAttributeSet::OnRep_LightningResistance(const FGameplayAttributeData& OldLightningResistance) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, LightningResistance, OldLightningResistance);
}

void UAuraAttributeSet::OnRep_PhysicalResistance(const FGameplayAttributeData& OldPhysicalResistance) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAuraAttributeSet, PhysicalResistance, OldPhysicalResistance);
}
